home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mint96sb.zoo / src / dosdir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-21  |  26.4 KB  |  1,188 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith. All rights reserved.
  3. */
  4.  
  5. /* DOS directory functions */
  6.  
  7. #include "mint.h"
  8.  
  9. /* change to a new drive: should always return a map of valid drives */
  10.  
  11. long ARGS_ON_STACK
  12. d_setdrv(d)
  13.     int d;
  14. {
  15.     long r;
  16.     extern long dosdrvs;    /* in filesys.c */
  17.  
  18.     r = drvmap() | dosdrvs | PSEUDODRVS;
  19.  
  20.     TRACE(("Dsetdrv(%d)", d));
  21.     if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) {
  22.         DEBUG(("Dsetdrv: invalid drive %d", d));
  23.         return r;
  24.     }
  25.  
  26.     curproc->base->p_defdrv = curproc->curdrv = d;
  27.     return r;
  28. }
  29.  
  30. long ARGS_ON_STACK
  31. d_getdrv()
  32. {
  33.     TRACE(("Dgetdrv"));
  34.     return curproc->curdrv;
  35. }
  36.  
  37. long ARGS_ON_STACK
  38. d_free(buf, d)
  39.     long *buf;
  40.     int d;
  41. {
  42.     fcookie *dir;
  43.     extern int aliasdrv[];
  44.  
  45.     TRACE(("Dfree(%d)", d));
  46.  
  47. /* drive 0 means current drive, otherwise it's d-1 */
  48.     if (d)
  49.         d = d-1;
  50.     else
  51.         d = curproc->curdrv;
  52.  
  53. /* Hack to make programs (like df) which use drive
  54.  * information from Fxattr() work more often.
  55.  */
  56.     if (d < 0 || d >= NUM_DRIVES) {
  57.         int i;
  58.  
  59.         for (i = 0; i < NUM_DRIVES; i++) {
  60.             if (aliasdrv[i] == d) {
  61.                 d = i;
  62.                 goto aliased;
  63.             }
  64.         }
  65.         return EDRIVE;
  66.     }
  67.  
  68. /* check for a media change -- we don't care much either way, but it
  69.  * does keep the results more accurate
  70.  */
  71.     (void)disk_changed(d);
  72.  
  73. aliased:
  74.  
  75. /* use current directory, not root, since it's more likely that
  76.  * programs are interested in the latter (this makes U: work much
  77.  * better)
  78.  */
  79.     dir = &curproc->curdir[d];
  80.     if (!dir->fs) {
  81.         DEBUG(("Dfree: bad drive"));
  82.         return EDRIVE;
  83.     }
  84.  
  85.     return (*dir->fs->dfree)(dir, buf);
  86. }
  87.  
  88. /* temp1 is a convenient place for path2fs puts the last component of
  89.  *   the path name
  90.  */
  91.  
  92. extern char temp1[PATH_MAX];    /* in filesys.c */
  93.  
  94. long ARGS_ON_STACK
  95. d_create(path)
  96.     const char *path;
  97. {
  98.     fcookie dir;
  99.     long r;
  100.  
  101.     TRACE(("Dcreate(%s)", path));
  102.  
  103.     r = path2cookie(path, temp1, &dir);
  104.     if (r) {
  105.         DEBUG(("Dcreate(%s): returning %ld", path, r));
  106.         return r;    /* an error occured */
  107.     }
  108. /* check for write permission on the directory */
  109.     r = dir_access(&dir, S_IWOTH);
  110.     if (r) {
  111.         DEBUG(("Dcreate(%s): access to directory denied",path));
  112.         return r;
  113.     }
  114.     return (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask);
  115. }
  116.  
  117. long ARGS_ON_STACK
  118. d_delete(path)
  119.     const char *path;
  120. {
  121.     fcookie parentdir, targdir;
  122.     long r;
  123.     PROC *p;
  124.     int i;
  125.     XATTR xattr;
  126.  
  127.     TRACE(("Ddelete(%s)", path));
  128.  
  129.     r = path2cookie(path, temp1, &parentdir);
  130.  
  131.     if (r) {
  132.         DEBUG(("Ddelete(%s): error %lx", path, r));
  133.         return r;
  134.     }
  135. /* check for write permission on the directory which the target
  136.  * is located
  137.  */
  138.     if ((r = dir_access(&parentdir, S_IWOTH)) != 0) {
  139.         DEBUG(("Ddelete(%s): access to directory denied", path));
  140.         return r;
  141.     }
  142.  
  143. /* now get the info on the file itself */
  144.  
  145.     r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0);
  146.     if (r || (r = (*targdir.fs->getxattr)(&targdir, &xattr)) != 0) {
  147.         DEBUG(("Ddelete: error %ld on %s", r, path));
  148.         return r;
  149.     }
  150.  
  151. /* if the "directory" is a symbolic link, really unlink it */
  152.     if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  153.         return (*parentdir.fs->remove)(&parentdir, temp1);
  154.     }
  155.     if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
  156.         DEBUG(("Ddelete: %s is not a directory", path));
  157.         return EPTHNF;
  158.     }
  159.  
  160. /* don't delete anyone else's root or current directory */
  161.     for (p = proclist; p; p = p->gl_next) {
  162.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  163.             continue;
  164.         for (i = 0; i < NUM_DRIVES; i++) {
  165.             if (samefile(&targdir, &p->root[i])) {
  166.                 DEBUG(("Ddelete: directory %s is a root directory",
  167.                     path));
  168.                 return EACCDN;
  169.             } else if (samefile(&targdir, &p->curdir[i])) {
  170.                 if (i == p->curdrv && p != curproc) {
  171.                     DEBUG(("Ddelete: directory %s is in use",
  172.                         path));
  173.                     return EACCDN;
  174.                 } else {
  175.                     p->curdir[i] = p->root[i];
  176.                 } 
  177.             }
  178.         }
  179.     }
  180.  
  181.     return (*parentdir.fs->rmdir)(&parentdir, temp1);
  182. }
  183.  
  184. long ARGS_ON_STACK
  185. d_setpath(path)
  186.     const char *path;
  187. {
  188.     fcookie dir;
  189.     int drv = curproc->curdrv;
  190.     int i;
  191.     char c;
  192.     long r;
  193.     XATTR xattr;
  194.  
  195.     TRACE(("Dsetpath(%s)", path));
  196.  
  197.     r = path2cookie(path, follow_links, &dir);
  198.  
  199.     if (r) {
  200.         DEBUG(("Dsetpath(%s): returning %ld", path, r));
  201.         return r;
  202.     }
  203.  
  204.     if (path[0] && path[1] == ':') {
  205.         c = *path;
  206.         if (c >= 'a' && c <= 'z')
  207.             drv = c-'a';
  208.         else if (c >= 'A' && c <= 'Z')
  209.             drv = c-'A';
  210.     }
  211.  
  212.     r = (*dir.fs->getxattr)(&dir, &xattr);
  213.  
  214.     if (r < 0) {
  215.         DEBUG(("Dsetpath: file '%s': attributes not found", path));
  216.         return r;
  217.     }
  218.  
  219.     if (!(xattr.attr & FA_DIR)) {
  220.         DEBUG(("Dsetpath(%s): not a directory",path));
  221.         return EPTHNF;
  222.     }
  223.  
  224. /*
  225.  * watch out for symbolic links; if c:\foo is a link to d:\bar, then
  226.  * "cd c:\foo" should also change the drive to d:
  227.  */
  228.     if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) {
  229.         for (i = 0; i < NUM_DRIVES; i++) {
  230.             if (curproc->root[i].dev == dir.dev &&
  231.                 curproc->root[i].fs == dir.fs) {
  232.                 if (drv == curproc->curdrv)
  233.                     curproc->curdrv = i;
  234.                 drv = i;
  235.                 break;
  236.             }
  237.         }
  238.     }
  239.     curproc->curdir[drv] = dir;
  240.     return 0;
  241. }
  242.  
  243. long ARGS_ON_STACK
  244. d_getpath(path, drv)
  245.     char *path;
  246.     int drv;
  247. {
  248.     fcookie *dir, *root;
  249.  
  250.     TRACE(("Dgetpath(%c)", drv + '@'));
  251.     if (drv < 0 || drv > NUM_DRIVES)
  252.         return EDRIVE;
  253.  
  254.     drv = (drv == 0) ? curproc->curdrv : drv-1;
  255.  
  256.     root = &curproc->root[drv];
  257.  
  258.     if (!root->fs) {    /* maybe not initialized yet? */
  259.         changedrv(drv);
  260.         root = &curproc->curdir[drv];
  261.         if (!root->fs)
  262.             return EDRIVE;
  263.     }
  264.     dir = &curproc->curdir[drv];
  265.  
  266.     return (*root->fs->getname)(root, dir, path);
  267. }
  268.  
  269. /* jr: like d_getpath, except that the caller provides a limit
  270.    for the max. number of characters to be put into the buffer.
  271.    Inspired by POSIX.1, getcwd(), 5.2.2 */
  272.  
  273. long ARGS_ON_STACK
  274. d_getcwd(path, drv, size)
  275.     char *path;
  276.     int drv, size;
  277. {
  278.     long ret;
  279.     char buf[PATH_MAX];
  280.     
  281.     TRACE (("Dgetcwd(%c,%d)", drv + '@', size));
  282.     
  283.     if (0 != (ret = d_getpath (buf, drv)))
  284.         return ret;
  285.             
  286.     /* all went well */
  287.     
  288.     if (strlen (buf) < size) {
  289.         strcpy (path, buf);
  290.         return 0L;
  291.     } else
  292.         return ERANGE;
  293. }
  294.  
  295. long ARGS_ON_STACK
  296. f_setdta(dta)
  297.     DTABUF *dta;
  298. {
  299.  
  300.     TRACE(("Fsetdta: %lx", dta));
  301.     curproc->dta = dta;
  302.     curproc->base->p_dta = (char *)dta;
  303.     return 0;
  304. }
  305.  
  306. long ARGS_ON_STACK
  307. f_getdta()
  308. {
  309.     long r;
  310.  
  311.     r = (long)curproc->dta;
  312.     TRACE(("Fgetdta: returning %lx", r));
  313.     return r;
  314. }
  315.  
  316. /*
  317.  * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
  318.  */
  319.  
  320. long ARGS_ON_STACK
  321. f_sfirst(path, attrib)
  322.     const char *path;
  323.     int attrib;
  324. {
  325.     char *s, *slash;
  326.     FILESYS *fs;
  327.     fcookie dir, newdir;
  328.     DTABUF *dta;
  329.     DIR *dirh;
  330.     XATTR xattr;
  331.     long r;
  332.     int i, havelabel;
  333.  
  334.     TRACE(("Fsfirst(%s, %x)", path, attrib));
  335.  
  336.     r = path2cookie(path, temp1, &dir);
  337.  
  338.     if (r) {
  339.         DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
  340.         return r;
  341.     }
  342.  
  343. /*
  344.  * we need to split the last name (which may be a pattern) off from
  345.  * the rest of the path, even if FS_KNOPARSE is true
  346.  */
  347.     slash = 0;
  348.     s = temp1;
  349.     while (*s) {
  350.         if (*s == '\\')
  351.             slash = s;
  352.         s++;
  353.     }
  354.  
  355.     if (slash) {
  356.         *slash++ = 0;    /* slash now points to a name or pattern */
  357.         r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0);
  358.         if (r) {
  359.             DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
  360.             return r;
  361.         }
  362.         dir = newdir;
  363.     } else {
  364.         slash = temp1;
  365.     }
  366.  
  367. /* BUG? what if there really is an empty file name? */
  368.     if (!*slash) {
  369.         DEBUG(("Fsfirst: empty pattern"));
  370.         return EFILNF;
  371.     }
  372.  
  373.     fs = dir.fs;
  374.     dta = curproc->dta;
  375.  
  376. /* Now, see if we can find a DIR slot for the search. We use the following
  377.  * heuristics to try to avoid destroying a slot:
  378.  * (1) if the search doesn't use wildcards, don't bother with a slot
  379.  * (2) if an existing slot was for the same DTA address, re-use it
  380.  * (3) if there's a free slot, re-use it. Slots are freed when the
  381.  *     corresponding search is terminated.
  382.  */
  383.  
  384.     for (i = 0; i < NUM_SEARCH; i++) {
  385.         if (curproc->srchdta[i] == dta) {
  386.             dirh = &curproc->srchdir[i];
  387.             if (dirh->fc.fs) {
  388.                 (*dirh->fc.fs->closedir)(dirh);
  389.                 dirh->fc.fs = 0;
  390.             }
  391.             curproc->srchdta[i] = 0; /* slot is now free */
  392.         }
  393.     }
  394.  
  395. /* copy the pattern over into dta_pat into TOS 8.3 form */
  396. /* remember that "slash" now points at the pattern (it follows the last \,
  397.    if any)
  398.  */
  399.     copy8_3(dta->dta_pat, slash);
  400.  
  401. /* if attrib & FA_LABEL, read the volume label */
  402. /* BUG: the label date and time are wrong. Does it matter?
  403.  */
  404.     havelabel = 0;
  405.     if (attrib & FA_LABEL) {
  406.         r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN);
  407.         dta->dta_attrib = FA_LABEL;
  408.         dta->dta_time = dta->dta_date = 0;
  409.         dta->dta_size = 0;
  410.         dta->magic = EVALID;
  411.         if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat))
  412.             r = EFILNF;
  413.         if (attrib == FA_LABEL)
  414.             return r;
  415.         else if (r == 0)
  416.             havelabel = 1;
  417.     }
  418.  
  419.     if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */
  420.         r = relpath2cookie(&dir, slash, follow_links, &newdir, 0);
  421.         if (r == 0) {
  422.             r = (*newdir.fs->getxattr)(&newdir, &xattr);
  423.         }
  424.         if (r) {
  425.             DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
  426.             return r;
  427.         }
  428.         dta->magic = EVALID;
  429.         dta->dta_attrib = xattr.attr;
  430.         dta->dta_time = xattr.mtime;
  431.         dta->dta_date = xattr.mdate;
  432.         dta->dta_size = xattr.size;
  433.         strncpy(dta->dta_name, slash, TOS_NAMELEN-1);
  434.         dta->dta_name[TOS_NAMELEN-1] = 0;
  435.         if (curproc->domain == DOM_TOS &&
  436.             !(fs->fsflags & FS_CASESENSITIVE))
  437.             strupr(dta->dta_name);
  438.  
  439.         return 0;
  440.     }
  441.  
  442. /* There is a wild card. Try to find a slot for an opendir/readdir
  443.  * search. NOTE: we also come here if we were asked to search for
  444.  * volume labels and found one.
  445.  */
  446.     for (i = 0; i < NUM_SEARCH; i++) {
  447.         if (curproc->srchdta[i] == 0)
  448.             break;
  449.     }
  450.     if (i == NUM_SEARCH) {
  451.         int oldest = 0; long oldtime = curproc->srchtim[0];
  452.  
  453.         DEBUG(("Fsfirst(%s): having to re-use a directory slot!",path));
  454.         for (i = 1; i < NUM_SEARCH; i++) {
  455.             if (curproc->srchtim[i] < oldtime) {
  456.                 oldest = i;
  457.                 oldtime = curproc->srchtim[i];
  458.             }
  459.         }
  460.     /* OK, close this directory for re-use */
  461.         i = oldest;
  462.         dirh = &curproc->srchdir[i];
  463.         if (dirh->fc.fs) {
  464.             (*dirh->fc.fs->closedir)(dirh);
  465.             dirh->fc.fs = 0;
  466.         }
  467.         curproc->srchdta[i] = 0;
  468.     }
  469.  
  470. /* check to see if we have read permission on the directory (and make
  471.  * sure that it really is a directory!)
  472.  */
  473.     r = dir_access(&dir, S_IROTH);
  474.     if (r) {
  475.         DEBUG(("Fsfirst(%s): access to directory denied",path));
  476.         return r;
  477.     }
  478.  
  479. /* set up the directory for a search */
  480.     dirh = &curproc->srchdir[i];
  481.     dirh->fc = dir;
  482.     dirh->index = 0;
  483.     dirh->flags = TOS_SEARCH;
  484.     r = (*dir.fs->opendir)(dirh, dirh->flags);
  485.     if (r != 0) {
  486.         DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)",
  487.             path, r));
  488.         return r;
  489.     }
  490.  
  491. /* mark the slot as in-use */
  492.     curproc->srchdta[i] = dta;
  493.  
  494. /* set up the DTA for Fsnext */
  495.     dta->index = i;
  496.     dta->magic = SVALID;
  497.     dta->dta_sattrib = attrib;
  498.  
  499. /* OK, now basically just do Fsnext, except that instead of ENMFIL we
  500.  * return EFILNF.
  501.  * NOTE: If we already have found a volume label from the search above,
  502.  * then we skip the f_snext and just return that.
  503.  */
  504.     if (havelabel)
  505.         return 0;
  506.  
  507.     r = f_snext();
  508.     if (r == ENMFIL) r = EFILNF;
  509.     if (r)
  510.         TRACE(("Fsfirst: returning %ld", r));
  511.     return r;
  512. }
  513.  
  514. /*
  515.  * Counter for Fsfirst/Fsnext, so that we know which search slots are
  516.  * least recently used. This is updated once per second by the code
  517.  * in timeout.c.
  518.  * BUG: 1/second is pretty low granularity
  519.  */
  520.  
  521. long searchtime;
  522.  
  523. long ARGS_ON_STACK
  524. f_snext()
  525. {
  526.     static char buf[TOS_NAMELEN+1];
  527.     DTABUF *dta = curproc->dta;
  528.     FILESYS *fs;
  529.     fcookie fc;
  530.     int i;
  531.     DIR *dirh;
  532.     long r;
  533.     XATTR xattr;
  534.  
  535.     TRACE(("Fsnext"));
  536.  
  537.     if (dta->magic == EVALID) {
  538.         DEBUG(("Fsnext: DTA marked a failing search"));
  539.         return ENMFIL;
  540.     }
  541.     if (dta->magic != SVALID) {
  542.         DEBUG(("Fsnext: dta incorrectly set up"));
  543.         return EINVFN;
  544.     }
  545.  
  546.     i = dta->index;
  547.     dirh = &curproc->srchdir[i];
  548.     curproc->srchtim[i] = searchtime;
  549.  
  550.     fs = dirh->fc.fs;
  551.     if (!fs)        /* oops -- the directory got closed somehow */
  552.         return EINTRN;
  553.  
  554. /* BUG: f_snext and readdir should check for disk media changes */
  555.  
  556.     for(;;) {
  557.         r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc);
  558.  
  559.         if (r == ENAMETOOLONG) {
  560.             DEBUG(("Fsnext: name too long"));
  561.             continue;    /* TOS programs never see these names */
  562.         }
  563.         if (r != 0) {
  564. baderror:
  565.             if (dirh->fc.fs)
  566.                 (void)(*fs->closedir)(dirh);
  567.             dirh->fc.fs = 0;
  568.             curproc->srchdta[i] = 0;
  569.             dta->magic = EVALID;
  570.             if (r != ENMFIL)
  571.                 DEBUG(("Fsnext: returning %ld", r));
  572.             return r;
  573.         }
  574.  
  575.         if (!pat_match(buf, dta->dta_pat))
  576.             continue;    /* different patterns */
  577.  
  578.     /* check for search attributes */
  579.         r = (*fc.fs->getxattr)(&fc, &xattr);
  580.         if (r) {
  581.             DEBUG(("Fsnext: couldn't get file attributes"));
  582.             goto baderror;
  583.         }
  584.     /* if the file is a symbolic link, try to find what it's linked to */
  585.         if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  586.             char linkedto[PATH_MAX];
  587.             r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX);
  588.             if (r == 0) {
  589.             /* the "1" tells relpath2cookie that we read a link */
  590.                 r = relpath2cookie(&dirh->fc, linkedto,
  591.                     follow_links, &fc, 1);
  592.                 if (r == 0)
  593.                 r = (*fc.fs->getxattr)(&fc, &xattr);
  594.             }
  595.             if (r) {
  596.                 DEBUG(("Fsnext: couldn't follow link: error %ld",
  597.                     r));
  598.             }
  599.         }
  600.  
  601.     /* silly TOS rules for matching attributes */
  602.         if (xattr.attr == 0) break;
  603.         if (xattr.attr & 0x21) break;
  604.         if (dta->dta_sattrib & xattr.attr)
  605.             break;
  606.     }
  607.  
  608. /* here, we have a match */
  609.     dta->dta_attrib = xattr.attr;
  610.     dta->dta_time = xattr.mtime;
  611.     dta->dta_date = xattr.mdate;
  612.     dta->dta_size = xattr.size;
  613.     strcpy(dta->dta_name, buf);
  614.  
  615.     if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) {
  616.         strupr(dta->dta_name);
  617.     }
  618.     return 0;
  619. }
  620.  
  621. long ARGS_ON_STACK
  622. f_attrib(name, rwflag, attr)
  623.     const char *name;
  624.     int rwflag;
  625.     int attr;
  626. {
  627.     fcookie fc;
  628.     XATTR xattr;
  629.     long r;
  630.  
  631.     TRACE(("Fattrib(%s, %d)", name, attr));
  632.  
  633.     r = path2cookie(name, (char *)0, &fc);
  634.  
  635.     if (r) {
  636.         DEBUG(("Fattrib(%s): error %ld", name, r));
  637.         return r;
  638.     }
  639.  
  640.     r = (*fc.fs->getxattr)(&fc, &xattr);
  641.  
  642.     if (r) {
  643.         DEBUG(("Fattrib(%s): getxattr returned %ld", name, r));
  644.         return r;
  645.     }
  646.  
  647.     if (rwflag) {
  648.         if (attr & (FA_LABEL|FA_DIR)) {
  649.             DEBUG(("Fattrib(%s): illegal attributes specified",name));
  650.             return EACCDN;
  651.         } else if (curproc->euid && curproc->euid != xattr.uid) {
  652.             DEBUG(("Fattrib(%s): not the file's owner",name));
  653.             return EACCDN;
  654.         } else if (xattr.attr & (FA_LABEL|FA_DIR)) {
  655.             DEBUG(("Fattrib(%s): file is a volume label "
  656.                   "or directory",name));
  657.             return EACCDN;
  658.         }
  659.         return (*fc.fs->chattr)(&fc, attr);
  660.     } else {
  661.         return xattr.attr;
  662.     }
  663. }
  664.  
  665. long ARGS_ON_STACK
  666. f_delete(name)
  667.     const char *name;
  668. {
  669.     fcookie dir;
  670.     long r;
  671.  
  672.     TRACE(("Fdelete(%s)", name));
  673.  
  674.     r = path2cookie(name, temp1, &dir);
  675.  
  676.     if (r) {
  677.         DEBUG(("Fdelete: error %ld", r));
  678.         return r;
  679.     }
  680.  
  681. /* check for write permission on directory */
  682.     r = dir_access(&dir, S_IWOTH);
  683.     if (r) {
  684.         DEBUG(("Fdelete(%s): write access to directory denied",name));
  685.         return r;
  686.     }
  687. /* BUG: we should check here for a read-only file */
  688.     return (*dir.fs->remove)(&dir,temp1);
  689. }
  690.  
  691. long ARGS_ON_STACK
  692. f_rename(junk, old, new)
  693.     int junk;        /* ignored, for TOS compatibility */
  694.     const char *old, *new;
  695. {
  696.     fcookie olddir, newdir, oldfil;
  697.     XATTR xattr;
  698.     char temp2[PATH_MAX];
  699.     long r;
  700.  
  701.     UNUSED(junk);
  702.  
  703.     TRACE(("Frename(%s, %s)", old, new));
  704.  
  705.     r = path2cookie(old, temp2, &olddir);
  706.     if (r) {
  707.         DEBUG(("Frename(%s,%s): error parsing old name",old,new));
  708.         return r;
  709.     }
  710. /* check for permissions on the old file
  711.  * GEMDOS doesn't allow rename if the file is FA_RDONLY
  712.  * we enforce this restriction only on regular files; processes,
  713.  * directories, and character special files can be renamed at will
  714.  */
  715.     r = relpath2cookie(&olddir, temp2, follow_links, &oldfil, 0);
  716.     if (r) {
  717.         DEBUG(("Frename(%s,%s): old file not found",old,new));
  718.         return r;
  719.     }
  720.     r = (*oldfil.fs->getxattr)(&oldfil, &xattr);
  721.     if (r ||
  722.         ((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) )
  723.     {
  724.         DEBUG(("Frename(%s,%s): access to old file not granted",old,new));
  725.         return EACCDN;
  726.     }
  727.     r = path2cookie(new, temp1, &newdir);
  728.     if (r) {
  729.         DEBUG(("Frename(%s,%s): error parsing new name",old,new));
  730.         return r;
  731.     }
  732.  
  733.     if (newdir.fs != olddir.fs) {
  734.         DEBUG(("Frename(%s,%s): different file systems",old,new));
  735.         return EXDEV;    /* cross device rename */
  736.     }
  737.  
  738. /* check for write permission on both directories */
  739.     r = dir_access(&olddir, S_IWOTH);
  740.     if (!r) r = dir_access(&newdir, S_IWOTH);
  741.     if (r) {
  742.         DEBUG(("Frename(%s,%s): access to a directory denied",old,new));
  743.         return r;
  744.     }
  745.     return (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1);
  746. }
  747.  
  748. /*
  749.  * GEMDOS extension: Dpathconf(name, which)
  750.  * returns information about filesystem-imposed limits; "name" is the name
  751.  * of a file or directory about which the limit information is requested;
  752.  * "which" is the limit requested, as follows:
  753.  *    -1    max. value of "which" allowed
  754.  *    0    internal limit on open files, if any
  755.  *    1    max. number of links to a file    {LINK_MAX}
  756.  *    2    max. path name length        {PATH_MAX}
  757.  *    3    max. file name length        {NAME_MAX}
  758.  *    4    no. of bytes in atomic write to FIFO {PIPE_BUF}
  759.  *    5    file name truncation rules
  760.  *    6    file name case translation rules
  761.  *
  762.  * unlimited values are returned as 0x7fffffffL
  763.  *
  764.  * see also Sysconf() in dos.c
  765.  */
  766.  
  767. long ARGS_ON_STACK
  768. d_pathconf(name, which)
  769.     const char *name;
  770.     int which;
  771. {
  772.     fcookie dir;
  773.     long r;
  774.  
  775.     r = path2cookie(name, (char *)0, &dir);
  776.     if (r) {
  777.         DEBUG(("Dpathconf(%s): bad path",name));
  778.         return r;
  779.     }
  780.     r = (*dir.fs->pathconf)(&dir, which);
  781.     if (which == DP_CASE && r == EINVFN) {
  782.     /* backward compatibility with old .XFS files */
  783.         return (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS :
  784.                 DP_CASEINSENS;
  785.     }
  786.     return r;
  787. }
  788.  
  789. /*
  790.  * GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new,
  791.  * POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for
  792.  * arbitrary length file names
  793.  */
  794.  
  795. long ARGS_ON_STACK
  796. d_opendir(name, flag)
  797.     const char *name;
  798.     int flag;
  799. {
  800.     DIR *dirh;
  801.     fcookie dir;
  802.     long r;
  803.  
  804.     r = path2cookie(name, follow_links, &dir);
  805.     if (r) {
  806.         DEBUG(("Dopendir(%s): error %ld", name, r));
  807.         return r;
  808.     }
  809.     r = dir_access(&dir, S_IROTH);
  810.     if (r) {
  811.         DEBUG(("Dopendir(%s): read permission denied", name));
  812.         return r;
  813.     }
  814.  
  815.     dirh = (DIR *)umalloc(SIZEOF(DIR));
  816.     if (!dirh) return ENSMEM;
  817.  
  818.     dirh->fc = dir;
  819.     dirh->index = 0;
  820.     dirh->flags = flag;
  821.     r = (*dir.fs->opendir)(dirh, flag);
  822.     if (r) {
  823.         DEBUG(("d_opendir(%s): opendir returned %ld", name, r));
  824.         ufree(dirh);
  825.         return r;
  826.     }
  827.     return (long)dirh;
  828. }
  829.  
  830. long ARGS_ON_STACK
  831. d_readdir(len, handle, buf)
  832.     int len;
  833.     long handle;
  834.     char *buf;
  835. {
  836.     DIR *dirh = (DIR *)handle;
  837.     fcookie fc;
  838.  
  839.     if (!dirh->fc.fs)
  840.         return EIHNDL;
  841.     return (*dirh->fc.fs->readdir)(dirh, buf, len, &fc);
  842. }
  843.  
  844. long ARGS_ON_STACK
  845. d_rewind(handle)
  846.     long handle;
  847. {
  848.     DIR *dirh = (DIR *)handle;
  849.  
  850.     if (!dirh->fc.fs)
  851.         return EIHNDL;
  852.     return (*dirh->fc.fs->rewinddir)(dirh);
  853. }
  854.  
  855. long ARGS_ON_STACK
  856. d_closedir(handle)
  857.     long handle;
  858. {
  859.     long r;
  860.     DIR *dirh = (DIR *)handle;
  861.  
  862.     if (!dirh->fc.fs)
  863.         return EIHNDL;
  864.     r = (*dirh->fc.fs->closedir)(dirh);
  865.     dirh->fc.fs = 0;
  866.  
  867.     if (r) {
  868.         DEBUG(("Dclosedir: error %ld", r));
  869.     }
  870.     ufree(dirh);
  871.     return r;
  872. }
  873.  
  874. /*
  875.  * GEMDOS extension: Fxattr gets extended attributes for a file. "flag"
  876.  * is 0 if symbolic links are to be followed (like stat), 1 if not (like
  877.  * lstat).
  878.  */
  879.  
  880. long ARGS_ON_STACK
  881. f_xattr(flag, name, xattr)
  882.     int flag;
  883.     const char *name;
  884.     XATTR *xattr;
  885. {
  886.     fcookie fc;
  887.     long r;
  888.  
  889.     TRACE(("Fxattr(%d, %s)", flag, name));
  890.  
  891.     r = path2cookie(name, flag ? (char *)0 : follow_links, &fc);
  892.     if (r) {
  893.         DEBUG(("Fxattr(%s): path2cookie returned %ld", name, r));
  894.         return r;
  895.     }
  896.     r = (*fc.fs->getxattr)(&fc, xattr);
  897.     if (r) {
  898.         DEBUG(("Fxattr(%s): returning %ld", name, r));
  899.     }
  900.     return r;
  901. }
  902.  
  903. /*
  904.  * GEMDOS extension: Flink(old, new) creates a hard link named "new"
  905.  * to the file "old".
  906.  */
  907.  
  908. long ARGS_ON_STACK
  909. f_link(old, new)
  910.     const char *old, *new;
  911. {
  912.     fcookie olddir, newdir;
  913.     char temp2[PATH_MAX];
  914.     long r;
  915.  
  916.     TRACE(("Flink(%s, %s)", old, new));
  917.  
  918.     r = path2cookie(old, temp2, &olddir);
  919.     if (r) {
  920.         DEBUG(("Flink(%s,%s): error parsing old name",old,new));
  921.         return r;
  922.     }
  923.     r = path2cookie(new, temp1, &newdir);
  924.     if (r) {
  925.         DEBUG(("Flink(%s,%s): error parsing new name",old,new));
  926.         return r;
  927.     }
  928.  
  929.     if (newdir.fs != olddir.fs) {
  930.         DEBUG(("Flink(%s,%s): different file systems",old,new));
  931.         return EXDEV;    /* cross device link */
  932.     }
  933.  
  934. /* check for write permission on the destination directory */
  935.  
  936.     r = dir_access(&newdir, S_IWOTH);
  937.     if (r) {
  938.         DEBUG(("Flink(%s,%s): access to directory denied",old,new));
  939.         return r;
  940.     }
  941.     return (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1);
  942. }
  943.  
  944. /*
  945.  * GEMDOS extension: Fsymlink(old, new): create a symbolic link named
  946.  * "new" that contains the path "old".
  947.  */
  948.  
  949. long ARGS_ON_STACK
  950. f_symlink(old, new)
  951.     const char *old, *new;
  952. {
  953.     fcookie newdir;
  954.     long r;
  955.  
  956.     TRACE(("Fsymlink(%s, %s)", old, new));
  957.  
  958.     r = path2cookie(new, temp1, &newdir);
  959.     if (r) {
  960.         DEBUG(("Fsymlink(%s,%s): error parsing %s", old,new,new));
  961.         return r;
  962.     }
  963.     r = dir_access(&newdir, S_IWOTH);
  964.     if (r) {
  965.         DEBUG(("Fsymlink(%s,%s): access to directory denied",old,new));
  966.         return r;
  967.     }
  968.     return (*newdir.fs->symlink)(&newdir, temp1, old);
  969. }
  970.  
  971. /*
  972.  * GEMDOS extension: Freadlink(buflen, buf, linkfile):
  973.  * read the contents of the symbolic link "linkfile" into the buffer
  974.  * "buf", which has length "buflen".
  975.  */
  976.  
  977. long ARGS_ON_STACK
  978. f_readlink(buflen, buf, linkfile)
  979.     int buflen;
  980.     char *buf;
  981.     const char *linkfile;
  982. {
  983.     fcookie file;
  984.     long r;
  985.     XATTR xattr;
  986.  
  987.     TRACE(("Freadlink(%s)", linkfile));
  988.  
  989.     r = path2cookie(linkfile, (char *)0, &file);
  990.     if (r) {
  991.         DEBUG(("Freadlink: unable to find %s", linkfile));
  992.         return r;
  993.     }
  994.     r = (*file.fs->getxattr)(&file, &xattr);
  995.     if (r) {
  996.         DEBUG(("Freadlink: unable to get attributes for %s", linkfile));
  997.         return r;
  998.     }
  999.     if ( (xattr.mode & S_IFMT) == S_IFLNK )
  1000.         return (*file.fs->readlink)(&file, buf, buflen);
  1001.  
  1002.     DEBUG(("Freadlink: %s is not a link", linkfile));
  1003.     return EACCDN;
  1004. }
  1005.  
  1006. /*
  1007.  * GEMDOS extension: Dcntl(): do file system specific functions
  1008.  */
  1009.  
  1010. long ARGS_ON_STACK
  1011. d_cntl(cmd, name, arg)
  1012.     int cmd;
  1013.     const char *name;
  1014.     long arg;
  1015. {
  1016.     fcookie dir;
  1017.     long r;
  1018.  
  1019.     TRACE(("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg));
  1020.  
  1021.     r = path2cookie(name, temp1, &dir);
  1022.     if (r) {
  1023.         DEBUG(("Dcntl: couldn't find %s", name));
  1024.         return r;
  1025.     }
  1026.     return (*dir.fs->fscntl)(&dir, temp1, cmd, arg);
  1027. }
  1028.  
  1029. /*
  1030.  * GEMDOS extension: Fchown(name, uid, gid) changes the user and group
  1031.  * ownerships of a file to "uid" and "gid" respectively.
  1032.  */
  1033.  
  1034. long ARGS_ON_STACK
  1035. f_chown(name, uid, gid)
  1036.     const char *name;
  1037.     int uid, gid;
  1038. {
  1039.     fcookie fc;
  1040.     XATTR xattr;
  1041.     long r;
  1042.  
  1043.     TRACE(("Fchown(%s, %d, %d)", name, uid, gid));
  1044.  
  1045.     r = path2cookie(name, follow_links, &fc);
  1046.     if (r) {
  1047.         DEBUG(("Fchown(%s): error %ld", name, r));
  1048.         return r;
  1049.     }
  1050.  
  1051. /* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can
  1052.  * only change the ownership of a file that is owned by this user, to
  1053.  * the effective group id of the process
  1054.  */
  1055.     if (curproc->euid) {
  1056.         if (curproc->egid != gid) return EACCDN;
  1057.         r = (*fc.fs->getxattr)(&fc, &xattr);
  1058.         if (r) {
  1059.             DEBUG(("Fchown(%s): unable to get file attributes",name));
  1060.             return r;
  1061.         }
  1062.         if (xattr.uid != curproc->euid || xattr.uid != uid) {
  1063.             DEBUG(("Fchown(%s): not the file's owner",name));
  1064.             return EACCDN;
  1065.         }
  1066.     }
  1067.     return (*fc.fs->chown)(&fc, uid, gid);
  1068. }
  1069.  
  1070. /*
  1071.  * GEMDOS extension: Fchmod(file, mode) changes a file's access
  1072.  * permissions.
  1073.  */
  1074.  
  1075. long ARGS_ON_STACK
  1076. f_chmod(name, mode)
  1077.     const char *name;
  1078.     unsigned mode;
  1079. {
  1080.     fcookie fc;
  1081.     long r;
  1082.     XATTR xattr;
  1083.  
  1084.     TRACE(("Fchmod(%s, %o)", name, mode));
  1085.     r = path2cookie(name, follow_links, &fc);
  1086.     if (r) {
  1087.         DEBUG(("Fchmod(%s): error %ld", name, r));
  1088.         return r;
  1089.     }
  1090.     r = (*fc.fs->getxattr)(&fc, &xattr);
  1091.     if (r) {
  1092.         DEBUG(("Fchmod(%s): couldn't get file attributes",name));
  1093.         return r;
  1094.     }
  1095.     if (curproc->euid && curproc->euid != xattr.uid) {
  1096.         DEBUG(("Fchmod(%s): not the file's owner",name));
  1097.         return EACCDN;
  1098.     }
  1099.     r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT);
  1100.     if (r) DEBUG(("Fchmod: error %ld", r));
  1101.     return r;
  1102. }
  1103.  
  1104. /*
  1105.  * GEMDOS extension: Dlock(mode, dev): locks or unlocks access to
  1106.  * a BIOS device. "mode" is 0 for unlock, 1 for lock; "dev" is a
  1107.  * BIOS device (0 for A:, 1 for B:, etc.).
  1108.  * Returns: 0 if the operation was successful
  1109.  *          EACCDN if a lock attempt is made on a drive that is being
  1110.  *            used
  1111.  *        ELOCKED if the drive is locked by another process
  1112.  *        ENSLOCK if a program attempts to unlock a drive it
  1113.  *            hasn't locked.
  1114.  */
  1115.  
  1116. PROC *dlockproc[NUM_DRIVES];
  1117.  
  1118. long ARGS_ON_STACK
  1119. d_lock(mode, dev)
  1120.     int mode, dev;
  1121. {
  1122.     PROC *p;
  1123.     FILEPTR *f;
  1124.     int i;
  1125.     extern int aliasdrv[];
  1126.  
  1127.     TRACE(("Dlock(%x,%c:)", mode, dev+'A'));
  1128.     if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE;
  1129.     if (aliasdrv[dev]) {
  1130.         dev = aliasdrv[dev] - 1;
  1131.         if (dev < 0 || dev >= NUM_DRIVES)
  1132.             return EDRIVE;
  1133.     }
  1134.     if ( (mode&1) == 0) {    /* unlock */
  1135.         if (dlockproc[dev] == curproc) {
  1136.             dlockproc[dev] = 0;
  1137.             changedrv(dev);
  1138.             return 0;
  1139.         }
  1140.         DEBUG(("Dlock: no such lock"));
  1141.         return ENSLOCK;
  1142.     }
  1143.  
  1144. /* code for locking */
  1145. /* is the drive already locked? */
  1146.     if (dlockproc[dev]) {
  1147.         DEBUG(("Dlock: drive already locked"));
  1148.         return (dlockproc[dev] == curproc) ? 0 : ELOCKED;
  1149.     }
  1150. /* see if the drive is in use */
  1151.     for (p = proclist; p; p = p->gl_next) {
  1152.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  1153.             continue;
  1154.         for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
  1155.             if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) {
  1156.         DEBUG(("Dlock: process %d has an open handle on the drive", p->pid));
  1157.                 return EACCDN;
  1158.             }
  1159.         }
  1160. #if 0
  1161. /* this is probably being a bit _too_ paranoid */
  1162.         for (i = 0; i < NUM_SEARCH; i++) {
  1163.             dirh = &curproc->srchdir[i];
  1164.             if (dirh && dirh->fc.fs && dirh->fc.dev == dev) {
  1165.         DEBUG(("Dlock: process %d has an open directory on the drive",p->pid));
  1166.                 return EACCDN;
  1167.             }
  1168.         }
  1169. #endif
  1170.     }
  1171.  
  1172. /* if we reach here, the drive is not in use */
  1173. /* we lock it by setting dlockproc and by setting all root and current
  1174.  * directories referring to the device to a null file system
  1175.  */
  1176.     for (p = proclist; p; p = p->gl_next) {
  1177.         for (i = 0; i < NUM_DRIVES; i++) {
  1178.             if (p->root[i].dev == dev)
  1179.                 p->root[i].fs = 0;
  1180.             if (p->curdir[i].dev == dev)
  1181.                 p->curdir[i].fs = 0;
  1182.         }
  1183.     }
  1184.  
  1185.     dlockproc[dev] = curproc;
  1186.     return 0;
  1187. }
  1188.